#! /usr/bin/env bash

#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you under the Apache License, Version 2.0 (the
#  "License"); you may not use this file except in compliance
#  with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

TSQA_TSXS=${TSQA_TSXS:-/opt/ats/bin/tsxs}
TSQA_TESTNAME=$(basename $0)
source $(dirname $0)/functions

OPENSSL=${OPENSSL:-openssl}

check_openssl_version() {
  local vers=$($OPENSSL version)

  # Apparantly OpenSSL SNI was added in 0.9.8f, but I'm too lazy to
  # parse the version properly, so let's just say that 1.0 and greater is ok.
  case $vers in
  OpenSSL\ [1-9].[0-9].*) return 0;;
  *) return 1;;
  esac
}

make_ssl_certificate() {
  local cn="$1"
  local filename="$2"

  msg generating SSL key and certificate for "$cn"

  $OPENSSL genrsa -out ${cn}.key 2048
  $OPENSSL req -new -key ${cn}.key -out ${cn}.csr \
    -subj "/C=US/ST=CA/L=Norm/O=TrafficServer/OU=Test/CN=${cn}"
  $OPENSSL x509 -req -days 1 -in ${cn}.csr -signkey ${cn}.key -out ${cn}.crt

  cat ${cn}.crt ${cn}.key > ${filename}

  rm -rf ${cn}.csr ${cn}.key ${cn}.crt
}

openssl_verify_certificate() {
  local certname="$1" # Certificate CN to expect
  local result="$TSQA_ROOT/${certname}.result"
  local commonName=
  local status=1  # default status is FAIL

  shift
  msg "checking for the $certname certificate ..." | tee -a "$TSQA_ROOT/$TSQA_TESTNAME.log"

  # When s_client verifies the certificate, it will log a line that looks like:
  # depth=0 C = US, ST = CA, L = Norm, O = TrafficServer, OU = Test, CN = address.tsqa.trafficserver.apache.org
  $OPENSSL s_client "$@" < /dev/null > "$result" 2>&1
  if [ "$?" != 0 ]; then
    fail "openssl check for $certname failed"
  fi

  # The output of this openssl formulation is:
  # subject=
  #     countryName               = US
  #     stateOrProvinceName       = CA
  #     localityName              = Norm
  #     organizationName          = TrafficServer
  #     organizationalUnitName    = Test
  #     commonName                = *.tsqa.trafficserver.apache.org
  commonName=$($OPENSSL x509 -in "$result" -noout -subject -nameopt multiline | awk '/commonName/{print $3}')

  if [ "$commonName" != "$certname" ]; then
    fail "received certificate CN \"$commonName\", expected \"$certname\""
  fi

  if [ "$?" != 0 ]; then
    fail "certificate name $certname did not match"
  fi
}

check_openssl_version || fatal OpenSSL 1.0 or later is required

bootstrap

# If Traffic Server is not up, bring it up ...
alive cop || startup || fatal unable to start Traffic Server
trap shutdown 0 EXIT

for name in \
  \*.tsqa.trafficserver.apache.org \
  sni.tsqa.trafficserver.apache.org \
  port.tsqa.trafficserver.apache.org \
  address.tsqa.trafficserver.apache.org \
  default.tsqa.trafficserver.apache.org
do
  logexec make_ssl_certificate $name $TSQA_ROOT/$(sysconfdir)/${name}.pem \
    || fatal failed to generate SSL certificate for "$name"
done

cat > $TSQA_ROOT/$(sysconfdir)/ssl_multicert.config <<EOF
ssl_cert_name=sni.tsqa.trafficserver.apache.org.pem
ssl_cert_name=*.tsqa.trafficserver.apache.org.pem

ssl_cert_name=port.tsqa.trafficserver.apache.org.pem dest_ip=127.0.0.1:10443
ssl_cert_name=address.tsqa.trafficserver.apache.org.pem dest_ip=127.0.0.1
ssl_cert_name=default.tsqa.trafficserver.apache.org.pem dest_ip=*
EOF

# XXX hardcoding the ports is lame ...
PORT=9443:ssl,10443:ssl,11443:ssl

# Enable SSL and bounce Traffic Server.
tsexec traffic_line -s proxy.config.http.server_ports -v $PORT
tsexec traffic_line -s proxy.config.diags.debug.tags -v ssl

# The sleep is needed to let Traffic Server schedule the config change.
msgwait 2 to restart with SSL ports enabled
tsexec traffic_line -L

msgwait 6 for traffic_server to restart
alive server || startup || fatal unable to start Traffic Server

# debugging ...
# tsexec traffic_line -r proxy.config.diags.debug.tags
# tsexec traffic_line -r proxy.config.http.server_ports

# This should get *.tsqa.trafficserver.apache.org ...
openssl_verify_certificate '*.tsqa.trafficserver.apache.org' \
  -connect 127.0.0.1:9443 -servername wildcard.tsqa.trafficserver.apache.org

# This should get sni.tsqa.trafficserver.apache.org ...
openssl_verify_certificate 'sni.tsqa.trafficserver.apache.org' \
  -connect 127.0.0.1:9443 -servername sni.tsqa.trafficserver.apache.org

# This should get port.tsqa.trafficserver.apache.org ...
openssl_verify_certificate 'port.tsqa.trafficserver.apache.org' \
  -connect 127.0.0.1:10443

# This should get address.tsqa.trafficserver.apache.org ...
openssl_verify_certificate 'address.tsqa.trafficserver.apache.org' \
  -connect 127.0.0.1:9443

# XXX not sure how to get the default.tsqa.trafficserver.apache.org; need to listen on a second address for that.

exit $TSQA_FAIL

# vim: set sw=2 ts=2 et :
